home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 3 / Gold Medal Software - Volume 3 (Gold Medal) (1994).iso / prog / alertdrv.arj / MANUAL.TXT < prev    next >
Text File  |  1994-01-25  |  71KB  |  1,461 lines

  1.  
  2.  
  3.                                                 Logical Operators
  4.                                 Professional Software Development
  5.                                           & Computer Consultation
  6.                                                                  
  7.                                               707-A Dunbar Avenue
  8.                                                      P.O. Box 815
  9.                                             Dunbar WV  25064-0815
  10.                                          Telephone: (304)768-5079
  11.  
  12.                                 THE ALERTDRIVER C++ CLASS LIBRARY
  13.                                        EXCERPTS FROM USER'S GUIDE
  14.                                                      Version 1.00
  15.                                                                  
  16.   References to businesses, organizations, and individuals
  17.  within this publication are supplied only for informational
  18.     purposes and in no way imply or infer endorsement or
  19.   recommendation of the products and/or services of Logical
  20.  Operators or its associates or affiliates unless explicitly
  21.                            stated.
  22.    
  23.    All brand names, trademarks, and registered trademarks
  24.  appearing throughout this document are properties of their
  25.    respective owners. The AlertDriver Class Library source
  26.    code, object code, and all associated documentation are
  27.  copyrighted 1994 by Logical Operators. All Rights Reserved.
  28.                    Part number ADUG940125.
  29.  
  30.  
  31.  
  32. Preface
  33.  
  34.  
  35. At Logical Operators, we encountered many problems, portability
  36. issues, and maintenance issues when writing error-reporting
  37. code for the reusable libraries used in our applications.  Nested
  38. if-then-else code detracted from the clarity of algorithms,
  39. hard-coded error messages caused reusability problems, and
  40. re-routing messages from the screen to other devices (such as
  41. printers) was difficult to say the least.
  42.  
  43. Our solution to these problems, the AlertDriver Class Library,
  44. avoids all of these problems while providing a new software
  45. library with modularity, encapsulation, a consistent call
  46. interface, and efficient code size and speed. The AlertDriver
  47. Class Library is reusable and extensible to multiple application
  48. environments and hardware configurations. And perhaps most
  49. importantly, this easy-to-use class library provides the
  50. flexibility of multiple error-reporting methods within the same
  51. program while including the capability to easily enable, disable,
  52. or change reporting methods at both compile-time and run-time!
  53.  
  54.  
  55.  
  56. Chapter 2           AlertDriver Class Library Concepts
  57.                     
  58.  
  59. More Than Just Errors
  60.  
  61. The   AlertDriver  Class  Library  does  more  than  just  simple
  62. error-reporting,  it  alerts the user to  virtually  any  program
  63. condition   during   execution  (hence  the  name).   There   are
  64. several  types  of  program  conditions,  or  alerts,  which  you
  65. may  wish  to  present  to  the user  during  program  execution:
  66. errors, information, messages, and warnings.
  67.  
  68. Errors,  the  most  serious  types  of  alerts,  occur   when   a
  69. program  detects  a  state  which  makes  continuation   of   the
  70. current   branch   of   execution  impossible.   In   interactive
  71. applications,  an  error  usually stops program  execution  until
  72. acknowledged  by  the  user. A typical error  might  be:  "Cannot
  73. find file C:\YOURFILE.DAT."
  74.  
  75. Information  alerts  occur whenever an  application  informs  the
  76. user  of  a  program  condition or the occurrence  of  an  event.
  77. Information   alerts  do  not  reflect  execution  problems   and
  78. exist  only  for  the  convenience of the  user.  In  interactive
  79. applications,  an  information  alert  stops  program   execution
  80. until  acknowledged  by  the  user. A typical  information  alert
  81. might be:  "File C:\YOURFILE.DAT was successfully loaded."
  82.  
  83. Messages  also  occur when the application informs  the  user  of
  84. a   program  condition.  Like  information  alerts,  messages  do
  85. not   reflect   execution  problems  and  exist  only   for   the
  86. convenience  of  the  user. Unlike information  alerts,  messages
  87. do  not  stop  program  execution  in  interactive  applications.
  88. Once   posted,  a  message  remains  active  until  replaced   by
  89. another   message  or  cleared  by  the  application.  A   sample
  90. message might be:  "Loading file C:\YOURFILE.DAT..."
  91.  
  92. Warnings   (sometimes   referred   to   as   confirmations)   are
  93. reported   when   the  application  prompts  the   user   for   a
  94. decision.  Warnings  are  typically  posed  as  a  question  with
  95. three  possible  answers:   YES, NO, or  CANCEL.  Warnings  pause
  96. program   execution  until  the  user  makes  a  decision,   then
  97. continue   program   execution  based  upon  that   decision.   A
  98. common   warning   is   "Do   you   want   to   overwrite    file
  99. C:\YOURFILE.DAT?"
  100.  
  101. Although  the  AlertDriver Class Library provides  the  means  to
  102. deal  with  all  of  these  types of alerts,  this  chapter  will
  103. concentrate   only   on   error   handling   to   simplify    the
  104. discussion.  Please  keep  in mind that  the  concepts  of  error
  105. handling also apply to each of the other types of alerts.
  106.  
  107.  
  108. The AlertableObject Concept
  109.  
  110. In   most   class   libraries,  the  majority  of   classes   are
  111. ultimately  descendants  of a single root  class  (usually  named
  112. something    like    Object    and    having    little     actual
  113. functionality).  Most  new  classes  created  by  users  of   the
  114. library  inherit  the properties of an existing  class,  even  if
  115. the  class  is  as  basic  as  Object.  Thus,  most  objects  (or
  116. class   instantiations)  in  an  application  can  be  referenced
  117. and treated as a lowest common denominator - an Object.
  118.  
  119. Now  let's  suppose  that  the root  class  has  the  ability  to
  120. report   errors  it  encounters  during  method  executions.   If
  121. such   a   class   exists,  it  becomes  easy  to   derive   more
  122. specialized  classes  while  simply  inheriting  the  ability  to
  123. report  errors.  Our  programs can invoke  the  member  functions
  124. of  an  object,  knowing  that if an  error  occurs,  the  object
  125. itself  will  report  the  problem  to  the  user!  Our  programs
  126. only  need  to  be  aware  of  the  success  or  failure  of  the
  127. member function's execution.
  128.  
  129. If  such  a  capability could be added to the  root  class  of  a
  130. commercial  application  framework,  then  the  majority  of  the
  131. framework's  classes  (and their descendants)  would  be  capable
  132. of   reporting  their  errors  to  the  user  in  a  standardized
  133. manner   right   out   of   the  box   without   any   additional
  134. programming!   Of  course,  not  every  programmer   would   have
  135. access  to  the  framework's source code to implement  this,  and
  136. we   may   not  want  to  add  additional  overhead  to  such   a
  137. rudimentary  class  anyway.  So in the  true  spirit  of  object-
  138. oriented   programming,  we  would  design  a  new  class   which
  139. directly  inherits  the  capabilities of the  framework's  Object
  140. counterpart  and  adds  some  basic  error-handling  capabilities
  141. (see Example 4).
  142.  
  143. Example  4  - A sample class declaration which could  add  error-
  144. handling   capabilities  to  the  base  class  (Object)   of   an
  145. application  framework.  The AlertDriver  class  library  creates
  146. an error-handling class in a similar manner.
  147.  
  148. class AlertableObject : public Object
  149. {
  150.    /*add member functions below to
  151.      enable error handling*/
  152.    ...
  153. };   //class AlertableObject
  154.  
  155. The  AlertDriver  Class  Library  includes  a  class  similar  to
  156. that  in  Example  4  and implements the  class  in  such  a  way
  157. that  it  can  either  be  used  without  a  class  hierarchy  or
  158. integrated  into  practically any  C++  class  library:   new  or
  159. existing,  commercial  or  private.  We  have  named  this  class
  160. AlertableObject:   it  provides the functionality  to  alert  the
  161. user  of  errors.  By  serving as a base  class,  AlertableObject
  162. provides   a  common  calling  interface  to  our  error-handling
  163. member    functions.   In   addition,   because   AlertableObject
  164. contains   a   public  member  function  interface,  our   error-
  165. handling    routines   are   accessible   from    program    code
  166. throughout the application.
  167.  
  168.  
  169. Benefiting From AlertableObject
  170.  
  171. By   making   AlertableObject   a   direct   descendant   of    a
  172. framework's   root   class   in  the  class   hierarchy,   error-
  173. handling   capabilities  can  easily   be   inherited   by   your
  174. classes  and  their  descendants. Of course,  classes  which  are
  175. derived   from   the  root  class  (and  bypass  AlertableObject,
  176. like  the  existing classes in the framework)  will  not  inherit
  177. the error-handling capabilities.
  178.  
  179. So  what  other  benefits  exist from using  the  AlertableObject
  180. class  in  an  application? Suppose we have a code  module  which
  181. has   just   invoked  a  member  function  of  a  descendant   of
  182. AlertableObject.  Our  calling  module  does   not   report   any
  183. errors   which   occur  within  the  object's   member   function
  184. (that's   the  object's  responsibility).  However,  our  calling
  185. module   may   be  interested  in  determining  if   the   member
  186. function was able to execute successfully, and if not, why.
  187.  
  188. This   simple  scenario  is  an  excellent  reason  to  have   an
  189. object's  member  functions return error codes.  It  is  easy  to
  190. declare   a   group   of   constants   which   represent    error
  191. conditions  for  each  class  (see  Example  5).  Not  only  does
  192. this   practice  enforce  good  programming  by  removing   hard-
  193. coded  values  from  the member functions  and  calling  modules,
  194. but  a  wide  range  of  values can  be  represented  within  the
  195. code  returned  by  each member function. In  addition,  returned
  196. error   codes   remove   the  need  for  an  unnecessary   global
  197. variable  (such  as  errno) or class data  member  to  store  the
  198. most recent result of a method execution.
  199.  
  200. Example   5   -  Error  constants  can  easily  be  created   and
  201. returned from AlertableObjects.
  202.  
  203. const long int NOERR = 0;
  204. const long int FILENOTFOUND = 1;
  205. const long int OUTOFMEMORY = 2;
  206. ...
  207. class MyObj : public AlertableObject
  208. {
  209.    public:
  210.       virtual long int MemFunc1(void);
  211.          //returns error code
  212.    ...
  213. };   //class MyObj
  214.  
  215. By  testing  the  returned error codes, the module  calling  such
  216. member  functions  can  smoothly recover  from  errors  occurring
  217. within    the   object   while   avoiding   the   "matching-else"
  218. syndrome   for  error  reporting  (see  Example  6).  A  pleasant
  219. side-effect   is   that  the  source  code  becomes   much   more
  220. readable and understandable.
  221.  
  222. Example   6   -  The  same  algorithm  used  in  Example   1   is
  223. implemented   here,   using   calls   to   an   object's   member
  224. functions   rather  than  "plain"  function  calls.  The   object
  225. MyObj  is  derived  from  class AlertableObject,  allowing  MyObj
  226. to  report  errors  to  the  user when  they  occur.  Notice  how
  227. much  more  readable  the code below appears  than  the  code  of
  228. Example 1.
  229.  
  230. ...
  231. if (MyObj.func1() == NOERR)
  232.    if (MyObj.func2() == NOERR)
  233.       if (MyObj.func3() == NOERR)
  234.          ...
  235.  
  236. Perhaps  the  biggest  benefit of the  AlertableObject,  however,
  237. lies   in   its  encapsulation.  Because  the  object  is   self-
  238. contained  and  able  to  report its own errors  in  a  platform-
  239. independent   manner,   it   can   be   linked   into    multiple
  240. applications,  none  of  which has to  deal  with  reporting  the
  241. errors  which  could occur internally. Imagine  how  much  easier
  242. your  next  programming  project would be  if  you  could  simply
  243. plug  some  of  your existing classes together and  forget  about
  244. handling  the  errors,  confident  that  they  will  be   handled
  245. automatically! With the AlertDriver Class Library, you can!
  246.  
  247.  
  248. Introducing The AlertDriver
  249.  
  250. The  AlertDriver  is  the class in the hierarchy  which  provides
  251. the     platform-    and    hardware-dependent    error-reporting
  252. capabilities  to  objects  derived  from  AlertableObject.   Each
  253. object   derived   from  AlertableObject  uses   an   AlertDriver
  254. object   to   perform  the  actual  reporting   of   each   error
  255. message.  The  AlertDriver  object  determines  how  each   error
  256. message  is  reported  using  the  API  (application  programming
  257. interface)  calls  and  hardware available  to  the  application;
  258. hence,  each  AlertDriver  object is  an  "error  device  driver"
  259. for   the  application  environment.  Because  AlertDrivers   are
  260. implemented  as  classes, any AlertDriver can  be  subclassed  by
  261. the  application  programmer to change or  control  its  specific
  262. actions.
  263.  
  264. The  AlertDriver  class library supplies  classes  which  can  be
  265. used   to   report   errors   to  the  screen   for   interactive
  266. applications,  or  log  errors to a  printer  or  disk  file  for
  267. programs   which   normally   run  without   human   supervision.
  268. Additional  AlertDrivers can be obtained  or  written  for  other
  269. platforms  and/or  hardware  devices  and  linked  into  programs
  270. written  for  the  AlertDriver Class Library with  no  change  to
  271. the   underlying  application  logic  or  AlertableObject  source
  272. code.
  273.  
  274. With  the  concepts  described to this point and  their  inherent
  275. programming   power   and  flexibility,  most   class   libraries
  276. would   stop   here.  However,  the  AlertDriver  Class   Library
  277. provides   solutions  to  even  more  programming  problems.   An
  278. AlertDriver  object  itself  can  reference  another  AlertDriver
  279. object,  effectively  chaining,  or  linking,  the  AlertDrivers.
  280. By   linking   the  AlertDrivers  for  an  AlertableObject,   the
  281. AlertableObject  can  report  a single  error  in  multiple  ways
  282. (for  example,  an  error  can  be reported  to  the  screen  and
  283. logged  in  a  disk  file).  AlertDrivers  can  be  attached  and
  284. detached  from  AlertableObjects in  any  combination  at  either
  285. compile-time  or  run-time  for maximum  program  efficiency  and
  286. flexibility.
  287.  
  288. Each  AlertDriver  can  also  have  its  actions  controlled   at
  289. either   compile-time  or  run-time  via  a  set  of   processing
  290. flags.   Error-handling,   for  example,   can   be   temporarily
  291. turned  on  or  off  for each AlertDriver  without  the  need  to
  292. disconnect   or  destroy  the  driver.  Other  useful  processing
  293. flags,  which  allow  a tremendous amount  of  control  over  the
  294. AlertDriver during run-time, are also available.
  295.  
  296. The  AlertDriver  library also mirrors the  concepts  of  client-
  297. server   technology:   each  AlertableObject  can  be  considered
  298. to   be   a  client,  each  AlertDriver  a  server.  The   client
  299. initiates  an  action  (i.e.:   report  an  error  message)   and
  300. each  server  has  the ability to perform that  action.  Multiple
  301. clients  can  be  attached to a single server and  a  client  may
  302. be served by multiple servers.
  303.  
  304. The  ability  to  connect several AlertableObjects  to  the  same
  305. AlertDriver   allows   applications  to   retain   all   of   the
  306. advantages  previously  discussed  while  minimizing  the  amount
  307. of  memory  overhead  needed  to implement  those  features.  For
  308. example,   in   a   spreadsheet  application,  each   spreadsheet
  309. object  could  can  be derived from an AlertableObject.  If  each
  310. cell   required  its  own  AlertDriver,  then  available   memory
  311. would  decrease  rapidly  as the spreadsheet  grew.  However,  by
  312. allowing  each  cell  to share a common AlertDriver,  the  memory
  313. needed  to  implement  AlertDriver  processing  is  held   to   a
  314. small,   fixed   amount  (the  amount  needed  to  allocate   the
  315. common   AlertDriver).   Since  all  cells   in   a   spreadsheet
  316. normally   report   errors  in  the  same  manner   anyway,   the
  317. unnecessary  allocation  of  duplicate  AlertDriver  objects   is
  318. avoided.
  319.  
  320. In   the   next   chapter,  we  will  discuss  how  easily   your
  321. programs  can  use  the  AlertDriver Class  Library  as  we  step
  322. through a tutorial.
  323.  
  324.  
  325.  
  326. Chapter 3           An AlertDriver Tutorial
  327.                     
  328.  
  329. Creating New Objects
  330.  
  331. To  use  the  AlertDriver Class Library,  you  must  include  the
  332. following line in your source code file:
  333.  
  334. #include <alertobj.h>
  335.  
  336. The  ALERTOBJ.H  header  file contains the  declaration  for  the
  337. AlertableObject    class.    (The    implementation    of     the
  338. AlertableObject  class  is supplied in  a  separate  object  code
  339. file   which   is  platform-dependent  -  see  the  Compiling   &
  340. Linking  chapter  for  details  on  compiling  and  linking  your
  341. code with the supported compilers/platforms.)
  342.  
  343. To  create  classes  of your own, you simply  derive  your  class
  344. from  AlertableObject  using  public inheritance.  (For  purposes
  345. of  demonstration,  we  will present  a  simple,  and  admittedly
  346. contrived,   sample  class  named  Integer,   which   by   itself
  347. doesn't  really  do  anything  useful,  but  will  allow  us   to
  348. demonstrate   the  proper  syntax  and  implementation   of   the
  349. AlertDriver   library   concepts  -  sample  platform-independent
  350. source   code   implementing  Integer  is   included   with   the
  351. AlertDriver   Class  Library.)  Your  class  will   automatically
  352. inherit   the   member  functions  and  data  of  AlertableObject
  353. (see Example 7).
  354.  
  355. Example  7  -  To  derive  your own classes,  derive  your  class
  356. from AlertableObject using public inheritance.
  357.  
  358. class Integer : public AlertableObject
  359. {
  360.    ...
  361.    /*declare class-specific member
  362.      functions and data members here*/
  363.    ...
  364. };   //class Integer
  365.  
  366.  
  367. Programmer-Defined Codes, Detection, & Reporting
  368.  
  369. Once    you    have   inherited   the   capabilities    of    the
  370. AlertableObject   class,   you   will   want   to   access   that
  371. functionality   from  your  member  functions.  There   are   two
  372. basic  ways  of  using  the AlertableObject member  functions  to
  373. alert  the  user  of  program conditions:  the  "quick-and-dirty"
  374. method and the "preferred" method.
  375.  
  376. The Quick-And-Dirty Reporting Method
  377. The   quick-and-dirty  method  of  using  the  AlertDriver  Class
  378. Library   is   usually   used  during  program   testing   and/or
  379. debugging.  Using  this  method, your  object  passes  a  literal
  380. string  to  the  attached AlertDriver to alert the  user  of  the
  381. program's  condition.  The  quick-and-dirty  method  retains  the
  382. platform-independent   output  of  the   AlertDriver,   but   the
  383. messages  themselves  are hard-coded into  the  program,  clearly
  384. not  the  most  elegant solution. However,  this  method  is  the
  385. quickest  and  easiest  to  use  for  quickly  getting   a   test
  386. program  to  work  or  for  porting  existing  programs  to   the
  387. AlertDriver  Class  Library. Programs written  using  the  quick-
  388. and-dirty   method  can  easily  be  ported  to   the   preferred
  389. method later.
  390.  
  391. To  use  the  quick-and-dirty method,  your  code  calls  one  of
  392. the   Report*()   member   functions.   There   are   four   such
  393. functions   (see   the  Class  Library  Reference   chapter   for
  394. details),   one   for   each  type  of   alert:    ReportError(),
  395. ReportInfo(),      ReportMessage(),     and      ReportWarning().
  396. (ClearMessage(),  a  related function used  in  conjunction  with
  397. member   function  ReportMessage(),  clears  the   last   message
  398. posted.)
  399.  
  400. Example  8  shows  how  the  code  in  Integer::TestAlertDriver()
  401. makes  calls  to  these  functions, passing  literal  strings  to
  402. each;  the  AlertDriver  for  the Integer  object  reports  those
  403. strings to the appropriate output device/platform.
  404.  
  405. Example  8  -  Calling  the Report*() member functions  inherited
  406. from   AlertableObject.   Each  of  these   inherited   functions
  407. passes the string along to the object's AlertDriver.
  408.  
  409. void Integer::TestAlertDriver(void)
  410. {
  411.    ...
  412.    //test literal strings
  413.    ReportMessage("Literal:  This is a message string.");
  414.    ReportError("Literal:  This is an error string.");
  415.    ReportWarning("Literal:  This is a warning string.",
  416. adsYES);
  417.    ClearMessage();
  418. };   //member function Integer::TestAlertDriver
  419.  
  420. One  final  note  on  reporting alerts:  the  Report*()  function
  421. should  always  be  called  from the member  function  where  the
  422. alert   is   first  encountered  or  generated.  By  consistently
  423. following  this  suggestion, the code  which  calls  your  member
  424. functions  can  be  written to assume that any  alerts  occurring
  425. within  your  member  functions have  already  been  reported  by
  426. the time the function returns.
  427.  
  428.  
  429. The Preferred Reporting Method
  430.  
  431. The  preferred  method of reporting alerts requires  a  bit  more
  432. work  than  the  quick-and-dirty  method,  but  results  in  code
  433. which  is  infinitely  more  reusable  and  flexible.  We  highly
  434. suggest   that  any  new  programs  and/or  objects  be   written
  435. using   the   preferred  method  of  alert  reporting,   as   the
  436. preferred   method   results  in  programs   which   are   better
  437. structured than programs using the quick-and-dirty method.
  438.  
  439. Before  we  explain  how  to use the preferred  method,  we  must
  440. first  discuss  the  concepts of using  programmer-defined  codes
  441. for   the  alerts.  Each  unique  alert  for  a  class   can   be
  442. assigned  a  unique  constant  value  for  that  type  of   alert
  443. within   the  class.  For  example,  the  error  codes  for   the
  444. Integer  object  are numbered starting at one  (1).  Each  unique
  445. warning   alert  for  the  Integer  object  is  also  represented
  446. with  a  code,  with  the  first warning also  being  number  one
  447. (1).  The  fact  that both alerts have a code  numbered  one  (1)
  448. does  not  cause a conflict - each type of code is  used  in  its
  449. own   context.   Using  constants  (or  #defines)  to   represent
  450. these  codes  in  your  program will  help  both  you  and  other
  451. programmers    to   identify   the   different   contexts    when
  452. reviewing  your  code  (see  Example  9).  While  you   may   use
  453. negative  numbers  as  alert  code constants,  do  not  use  zero
  454. (0)  as  an  identifier for an alert code - zero  is  pre-defined
  455. by  the  AlertDriver Class Library for each  alert  type  as  the
  456. constants  errOK,  infoOK, msgOK, and  warnOK.  Alert  codes  are
  457. represented by long integers.
  458.  
  459. Example  9  -  Define constants to uniquely identify  each  alert
  460. within  a  class. Each constant for a particular  type  of  alert
  461. within   a  class  may  have  the  same  value  as  the  constant
  462. identifier  of  another  alert  type  in  the  same  class.  Each
  463. constant  may  also  be  equal to constants  of  the  same  alert
  464. type in other classes.
  465.  
  466. ...
  467. //define alert constants for class Integer
  468. const long errIntDivByZero = 1;
  469. const long errIntOverflow  = 2;
  470. const long infoIntConstruct = 1;
  471. const long msgIntDestruct = 1;
  472. const long warnIntChange = 1;
  473.    /*legal - different types of alerts*/
  474. ...
  475. //define alert constants for class DiskFile
  476. const long errDiskFileNotFound = 1;   //legal
  477. const long errDiskFileDiskFull = 1;
  478.    /*illegal - same error code (1) defined
  479.      twice for same class*/
  480. ...
  481.  
  482. Because  errors  are  the  most serious  types  of  alerts,  they
  483. should  be  returned  from  member functions  whenever  possible.
  484. This  means  each member function should return  the  error  code
  485. of  any  error  generated  within that function,  or  return  the
  486. error  code  returned  by  functions it  calls.  By  adhering  to
  487. this  principle,  the  program module  which  calls  your  member
  488. functions  will  always  receive  an  error  code  denoting   the
  489. first  error  encountered  during  execution  of  your  function.
  490. Of  course,  your  functions should return  errOK  if  no  errors
  491. occur.
  492.  
  493. The    use    of    programmer-defined   alert   codes    figures
  494. prominently  in  the  preferred  method  of  alert  presentation.
  495. Rather  than  call  the Report*() member functions,  your  member
  496. functions   call   the  inherited  Handle*()  member   functions.
  497. Each  Handle*()  member  function  corresponds  to  one  of   the
  498. Report*()   member  functions;  the  Handle*()  member  functions
  499. are:     HandleError(),   HandleInfo(),   HandleMessage(),    and
  500. HandleWarning().
  501.  
  502. Each  of  the Handle*() functions is passed an alert  code  as  a
  503. parameter.  This  alert  code  is  then  passed  to  one  of  the
  504. Get*Text()  member  functions, converted to text,  and  the  text
  505. is   passed  to  one  of  the  Report*()  functions.  Of  course,
  506. there  are  four  Get*Text()  member functions:   GetErrorText(),
  507. GetInfoText(), GetMessageText(), and GetWarningText().
  508. To  use  the  preferred  method of  alerting,  your  object  must
  509. declare  the  appropriate alert constants for  alerts  which  can
  510. be   generated   within  your  object.  You  then  override   the
  511. Get*Text()  functions  to  convert those  constants  to  strings.
  512. In   a   well-developed  class  hierarchy,  if  your   Get*Text()
  513. function  receives  an alert constant which  it  cannot  convert,
  514. it  should  call  the  overridden  Get*Text()  function,  as  the
  515. constant  may  represent  an  alert  which  was  defined   in   a
  516. parent  class.  The  original  Get*Text()  member  functions   in
  517. AlertableObject  produce  generic  messages  (i.e.:   "Error  422
  518. encountered.")   for  unrecognized  alert   codes.   Example   10
  519. shows   the   implementation   of   the   GetErrorText()   member
  520. function for the Integer class.
  521.  
  522. Example    10   -   Implementation   of   Integer::GetErrorText()
  523. member    function.   Notice   how   error   codes   which    are
  524. unrecognized  by  this class are passed back  to  the  overridden
  525. function.
  526.  
  527. ...
  528. //define error constants for class Integer
  529. const long errIntDivByZero = 1;
  530. const long errIntOverflow  = 2;
  531. ...
  532. class Integer : public AlertableObject
  533. {
  534.    ...
  535.    public:
  536.       virtual void GetErrorText(const long errorCode, char
  537. *text);
  538.    ...
  539. };  //class Integer
  540. ...
  541. void Integer::GetErrorText(const long errorCode, char *text)
  542. {
  543.    switch (errorCode)
  544.    {
  545.       case errIntDivByZero : strcpy(text, "Divide by zero in
  546. Integer object.");
  547.                              break;
  548.       case errIntOverflow  : strcpy(text, "Value too large
  549. for Integer object.");
  550.                              break;
  551.       default              :
  552. AlertableObject::GetErrorText(errorCode, text);
  553.                              break;
  554.    };   //switch statement to determine error text
  555. };   //member function Integer::GetErrorText
  556.  
  557. To  invoke  the  use  of your strings in your  member  functions,
  558. you  simply  call  the  appropriate  Handle*()  member  function,
  559. passing  the  correct  alert  constant.  As  always,  you  should
  560. call   the   appropriate  Handle*()  member  function  from   the
  561. member   function   where   the  alert  is   first   encountered.
  562. Because   the  Handle*()  member  functions  automatically   call
  563. the  Get*Text()  and Report*() member functions,  a  simple  call
  564. to  the  appropriate Handle*() member function  sets  the  entire
  565. chain of events into motion.
  566.  
  567. At   first,  this  may  seem  like  a  roundabout  way  of  doing
  568. things,   until  you  realize  the  amount  of  flexibility   and
  569. reusability   that  your  code  gains.  Because  the   Get*Text()
  570. member   functions  are  virtual,  you  can  override   them   in
  571. descendant   classes.  If  you  don't  like  the   text   for   a
  572. specific  alert  code, you can derive a new class  and  return  a
  573. different  string  for  the code, even  if  you  don't  have  the
  574. source  code  for  the  original  class.  The  Get*Text()  member
  575. functions  don't  even  have  to store  the  alert  text  in  the
  576. program;  the  functions can be written  to  retrieve  the  alert
  577. text  from  an  external text file. Using  an  external  file  to
  578. store  the  strings  for  each alert  allows  an  application  to
  579. use  a  minimal  amount  of  program memory  to  store  text.  In
  580. addition,   the  text  file  can  later  be  altered  to   update
  581. existing  alert  text  without  requiring  recompilation  of  the
  582. application.  Coupled  with  the platform-independent  output  of
  583. the     AlertDriver,     these    capabilities     give     every
  584. AlertableObject  maximum  flexibility  in  presenting  alerts  to
  585. the user.
  586.  
  587. Example   11   demonstrates  how  the  overloaded  "/"   operator
  588. handles errant attempts to divide Integer objects by zero.
  589.  
  590. Example   11   -   The   overloaded  operator   member   function
  591. Integer::operator   /()   calls   inherited    member    function
  592. HandleError()  whenever  it  detects  an  attempted   divide   by
  593. zero.  Invoking  the  HandleError()  member  function  causes  an
  594. eventual  call  to  the Integer::GetErrorText()  member  function
  595. shown in Example 10.
  596.  
  597. int Integer::operator / (int aValue)
  598. {
  599.    int result;   //holds the function result
  600.  
  601.    if (aValue)
  602.       //specified value is not 0
  603.       result = val / aValue;
  604.    else
  605.    {
  606.       HandleError(errIntDivByZero);
  607.       result = INT_MAX;
  608.    }
  609.    return result;
  610. };   //overloaded operator Integer::operator /
  611.  
  612.  
  613.  
  614. Changing & Sharing AlertDrivers
  615.  
  616. All   programs   linked  with  the  AlertDriver   Class   Library
  617. automatically    gain    access   to   a   default    AlertDriver
  618. referenced   by  the  defaultAlertDriver  pointer   (defined   in
  619. ALERTDRV.H).    This   global   AlertDriver   is    usually    an
  620. interactive,   screen-oriented  driver   which   provides   alert
  621. reporting   capabilities   for  the  software   environment   for
  622. which     the     program    was    targeted.    For     example,
  623. defaultAlertDriver  may  provide text-mode  output  for  a  text-
  624. mode   program,  but  will  use  Windows  dialog  boxes  if   the
  625. program  has  been  compiled  as a Windows  application.  Because
  626. defaultAlertDriver  is  available to  be  called  from  any  part
  627. of   your  application,  even  your  non-object  code  can   call
  628. defaultAlertDriver's   member   functions   (see   Example   12).
  629. Calling   one   of   the  Handle*()  member  functions   of   the
  630. AlertDriver   class  is  equivalent  to  calling   one   of   the
  631. AlertableObject::Report*()  member  functions  (see   the   Class
  632. Library Reference for details).
  633.  
  634. Example  12  -  Accessing defaultAlertDriver's  member  functions
  635. from  non-object  code.  Calls  made  in  this  way  are  roughly
  636. equivalent  to  the  quick-and-dirty method of  reporting  alerts
  637. from within objects.
  638.  
  639. #include <alertdrv.h>
  640. ...
  641. void main(void)
  642. {
  643.    ...
  644.    //test the defaultAlertDriver without an object
  645.    defaultAlertDriver->HandleInfo(
  646.       "Text from non-object code.");
  647.    ...
  648. };   //function main()
  649.  
  650. Each  AlertableObject (and derived object)  that  is  created  by
  651. your  program  initially  uses  the  defaultAlertDriver  as   its
  652. AlertDriver.    By    using   this   common   AlertDriver,    the
  653. AlertableObjects  in your program gain all  of  the  benefits  of
  654. platform-independent   error  reporting   with   a   minimum   of
  655. code/data  overhead.  One  AlertDriver  controls  the  alerts  of
  656. all your program's objects.
  657.  
  658. There  are  times,  however, when you may  want  to  change  this
  659. default   behavior,  particularly  if  the  default   AlertDriver
  660. does  not  provide  the  appropriate  type  of  output  for  your
  661. object.   For  example,  although  the  default  AlertDriver   is
  662. usually  screen-oriented,  your  may  want  your  object  to  log
  663. alerts to a disk file.
  664.  
  665. You  can  easily  change  the type of AlertDriver  used  by  your
  666. object                by               calling                the
  667. AlertableObject::ChangeLinkedAlertDriver()    member    function.
  668. To  call  this  inherited  member  function,  you  create  a  new
  669. AlertDriver  object,  then pass its address  as  a  parameter  to
  670. the  function.  Because  all  AlertDrivers  should  be  allocated
  671. on  the  heap,  this can usually be accomplished  with  one  line
  672. of  code.  For  example, the following line of code  will  change
  673. the   AlertDriver  for  object  MyObj  to  an  AlertDriver  which
  674. logs alerts to a text file named "myobj.log":
  675.  
  676. MyObj.ChangeLinkedAlertDriver(new
  677. TextFileAlertDriver("myobj.log", radFREERESOURCE));
  678.  
  679. (The  radFREERESOURCE  constant  forces  the  TextFileAlertDriver
  680. to  free  (close) the file whenever it is not in  use;  when  the
  681. file  is  closed  (not recording alerts), it can be  accessed  by
  682. other   TextFileAlertDrivers  or  other  applications.  See   the
  683. Class  Library  Reference  for more details  on  controlling  the
  684. TextFileAlertDriver.)
  685.  
  686. AlertDrivers  allocated  by your application  can  serve  several
  687. AlertableObjects  at  once,  just  as  the  default   AlertDriver
  688. originally  serves  all of the objects in your  program  as  they
  689. are  created.  Example 13 demonstrates how  to  share  one  newly
  690. allocated AlertDriver among two objects.
  691.  
  692. Example  13  -  Creating  an AlertDriver object  and  sharing  it
  693. among  multiple  objects. In this case,  both  objects  will  log
  694. their  alerts  to  the  same text file.   Remember,  AlertDrivers
  695. which   you   create  within  your  program  should   always   be
  696. allocated on the heap.
  697.  
  698. ...
  699. AlertDriver *pAlertDriver;
  700. Integer a, b;
  701. ...
  702. /*log a and b alerts to the same text file -
  703.   other AlertableObjects continue to use
  704.   defaultAlertDriver*/
  705. pAlertDriver =
  706.    new TextFileAlertDriver("myobjs.log",
  707.                            radFREERESOURCE);
  708. a.ChangeLinkedAlertDriver(pAlertDriver);
  709. b.ChangeLinkedAlertDriver(pAlertDriver);
  710. ...
  711.  
  712. To  completely  remove  (not  replace)  the  AlertDriver  for  an
  713. AlertableObject,          call          member           function
  714. ChangeLinkedAlertDriver()  with  a  parameter  of   NULL;   doing
  715. this   will   remove   all   alerting   capabilities   for    the
  716. AlertableObject.
  717.  
  718. The  AlertDriver  Class  Library  defines  the  standard  classes
  719. StdAlertDriver   and   TextFileAlertDriver   for   all   software
  720. platforms  and  environments. StdAlertDriver  uses  the  standard
  721. C++  iostreams  cin,  cout, and cerr  to  report  alerts  to  the
  722. user   while  TextFileAlertDriver  logs  alert  text  to  a  file
  723. which    is    specified    in   its   constructor.    Additional
  724. AlertDriver   classes   may   be  defined   for   your   specific
  725. software       environment       (for       example,        class
  726. TurboVisionAlertDriver   is   defined    for    programs    using
  727. Borland's  Turbo  Vision class library). See  the  Class  Library
  728. Reference   chapter   for  full  details   on   the   AlertDriver
  729. classes   which   are   available  in  your   specific   software
  730. environment.
  731.  
  732. Experienced  programmers  know that one  of  the  toughest  parts
  733. of  debugging  a  program  is  finding  heap  allocation  errors.
  734. Because  all  AlertDrivers are allocated  on  the  heap  and  may
  735. be   shared   among  multiple  AlertableObjects,   you   may   be
  736. wondering  if  there  is  an  easy  way  to  determine  when   an
  737. AlertDriver  is  no  longer being used and when  it  is  safe  to
  738. deallocate  an  AlertDriver. The answer  is  simple:   you  don't
  739. have   to   worry  about  deallocating  any  AlertDrivers,   even
  740. those   which   you  allocate!  The  AlertDriver  Class   Library
  741. handles it all for you!
  742.  
  743. Each   AlertDriver   contains  a   count   of   the   number   of
  744. AlertableObjects   using   its   services.    This    count    is
  745. incremented  each  time  an  AlertDriver  is  "attached"  to   an
  746. AlertableObject.  An  AlertableObject  is  "detached"   from   an
  747. AlertDriver   whenever   the  AlertableObject   is   deallocated,
  748. goes  out  of  scope,  or is attached to a different  AlertDriver
  749. object   through   member   function   ChangeLinkedAlertDriver();
  750. each  time  this  happens, the attached  AlertDriver's  count  is
  751. decremented.  When  the count reaches zero  (the  AlertDriver  is
  752. no   longer  being  used  by  any  object),  the  AlertDriver  is
  753. automatically  shutdown  and  deallocated.  What  this  means  to
  754. you   as   a  programmer  is  that  you  can  allocate  as   many
  755. AlertDrivers  of  as  many  different classes  as  necessary  and
  756. attach  them  to  as  many AlertableObjects as  you  desire  with
  757. the   understanding  that  each  of  the  AlertDrivers  will   be
  758. deallocated as soon as it is no longer being used.
  759.  
  760.  
  761. Linking AlertDrivers
  762.  
  763. Although  you  can  change  the  AlertDriver  used  for  each  of
  764. your  objects  at  any  time, this still may  not  give  you  the
  765. level  of  flexibility  you need. After  all,  it's  nice  to  be
  766. able   to   switch  between  screen-based  alerting  and  logging
  767. those  alerts  to  a  disk file, but what if  you  wanted  to  do
  768. both  at  the  same  time?  With the AlertDriver  Class  Library,
  769. you  can  attach  several  AlertDrivers  together  in  series  so
  770. that  the  same  alerts  are  reported  in  multiple  ways.  This
  771. process is known as "linking" the AlertDrivers.
  772.  
  773. To   link   one  AlertDriver  to  another,  you  must  call   the
  774. ChangeLinkedAlertDriver()   member   function   of   the    first
  775. AlertDriver  object.  (The first AlertDriver  object  is  defined
  776. as  the  AlertDriver  which is attached  to  an  AlertableObject,
  777. the   remaining   AlertDrivers   are   each   attached   to   the
  778. previously   linked  AlertDriver.  In  effect,  the  AlertDrivers
  779. form  a  linked  list,  often  referred  to  as  a  "chain.")   A
  780. common  use  of  linking AlertDrivers is  to  provide  disk  file
  781. logging   in   addition   to  screen  alerting   by   linking   a
  782. TextFileAlertDriver   to   an   AlertableObject's    AlertDriver.
  783. This    can    be    most    easily    accomplished    if     the
  784. AlertableObject's   AlertDriver  is   the   default   AlertDriver
  785. (see Example 14).
  786.  
  787. Example  14  -  By linking a TextFileAlertDriver to  the  default
  788. AlertDriver,  all  objects  using the  default  AlertDriver  will
  789. report  their  alerts  to  both  the  screen  and  a  disk   file
  790. (assuming  that  the default AlertDriver reports  alerts  to  the
  791. screen).
  792.  
  793. ...
  794. //attach a text file AlertDriver to the default driver
  795. defaultAlertDriver->ChangeLinkedAlertDriver(
  796.    new TextFileAlertDriver("adtest.log", radFREERESOURCE));
  797. ...
  798.  
  799. It    is    important    to    note   that    when    you    call
  800. AlertableObject::ChangeLinkedAlertDriver()                     or
  801. AlertDriver::ChangeLinkedAlertDriver(),   you   are   effectively
  802. replacing   all   AlertDrivers   which   are   linked   to    the
  803. object/driver   with   the   AlertDriver   you    specify.    For
  804. illustration,  assume  that  prior  to  calling   the   code   in
  805. Example   14,  defaultAlertDriver  is  the  first  of  four   (4)
  806. AlertDrivers  which  are  linked together.  After  executing  the
  807. code  in  Example  14, defaultAlertDriver will be  the  first  of
  808. two   (2)  AlertDrivers  in  the  link.  The  three  AlertDrivers
  809. which   were   linked   to  defaultAlertDriver   prior   to   the
  810. ChangeLinkedAlertDriver()     call     were     detached     from
  811. defaultAlertDriver  (and  possibly  deallocated  if   they   were
  812. not being used by other objects).
  813.  
  814. This  concept  works  in the other direction  as  well.  You  can
  815. link  several  AlertDrivers together, than pass  the  address  of
  816. the   first   one   to   ChangeLinkedAlertDriver();   the   first
  817. AlertDriver  will  be  directly  attached  to  the  object/driver
  818. for  which  the  call was made, while all other  AlertDrivers  in
  819. the    link   will   be   indirectly   attached   to   the   same
  820. object/driver.
  821.  
  822. When  several  AlertDrivers are linked together,  the  first  one
  823. processes  the  alert  from  the  AlertableObject,  then   passes
  824. the   alert   to  the  next  linked  AlertDriver.  This   process
  825. continues  until  there are no more AlertDrivers  in  the  chain.
  826. Because  of  this  series of actions, it is  suggested  that  you
  827. place    only    one   screen-oriented   AlertDriver    in    any
  828. AlertDriver   chain.   We  also  recommend   that   the   screen-
  829. oriented   AlertDriver  (or  any  AlertDriver   which   interacts
  830. with  the  user)  be placed first in the chain -  this  not  only
  831. allows  the  user  to  be  notified immediately  of  any  alerts,
  832. but  also  ensures  that  other AlertDrivers  in  the  chain  are
  833. informed   of   the   user's   choice.   For   example,   if   an
  834. AlertDriver   chain  contained  both  a  StdAlertDriver   and   a
  835. TextFileAlertDriver,   you   should  place   the   StdAlertDriver
  836. first   because  it  interacts  with  the  user.  In  this  case,
  837. warning  alerts  would  be  presented to  the  user  first,  then
  838. the  user's  choice  would be recorded  in  the  text  file.  Had
  839. the   TextFileAlertDriver  been  placed  first  in   the   chain,
  840. recording  the  user's choice correctly in the  text  file  would
  841. not be possible.
  842.  
  843.  
  844. Modifying AlertDriver Behavior
  845.  
  846. At  times,  you  may  wish to temporarily disable  processing  of
  847. a  certain  type of alert or affect the output of  an  alert  for
  848. an    AlertableObject   without   changing   or   detaching   the
  849. AlertDriver.   Such  changes  can  be  made   by   altering   the
  850. processing   flags   which  are  stored  in   each   AlertDriver.
  851. Through    the   use   of   AlertableObject   member    functions
  852. GetAlertProcFlags()   and   ChangeAlertProcFlags(),    you    can
  853. obtain  and  alter  the  settings of  the  linked  AlertDriver(s)
  854. for   an   object.  (You  can  directly  get/set  the  processing
  855. flags   for  an  AlertDriver  by  calling  its  member  functions
  856. GetProcFlags()  and  ChangeProcFlags() - see  the  Class  Library
  857. Reference chapter for details and syntax.)
  858.  
  859. The  processing  flags  for  an AlertDriver  are  represented  as
  860. bit-mapped  constants  prefixed with the  letters  "adf"  in  the
  861. ALERTDRV.H   header  file.  Four  of  these  constants   act   as
  862. switches   for   processing   the  various   types   of   alerts:
  863. adfERROR,  adfINFO,  adfMESSAGE,  and  adfWARNING.  When  one  of
  864. these  flags  is  enabled  (on), the  AlertDriver  processes  all
  865. alerts  of  that  type which are passed to it.  When  a  flag  is
  866. disabled  (off),  the AlertDriver ignores alerts  of  that  type,
  867. but  still  passes  the  alert  to the  next  linked  AlertDriver
  868. (if any).
  869.  
  870. The  return  value  of  AlertableObject::GetAlertProcFlags()   is
  871. an   unsigned   short  which  holds  the  value  of   all   flags
  872. currently  enabled  for  the  first linked  AlertDriver  for  the
  873. object.  You  can  test  to  see if a flag  is  enabled  (on)  by
  874. performing a binary "AND" operation:
  875.  
  876. //see if the first-linked AlertDriver for MyObj is currently
  877. processing warnings
  878. if (MyObj.GetAlertProcFlags() & adfWARNING)
  879. {
  880.    //warnings are being processed for MyObj
  881.    ...
  882. }
  883.  
  884. Example  15  shows  how  to enable or disable  processing  for  a
  885. specific        type       of       alert        using        the
  886. AlertableObject::ChangeAlertProcFlags()  member   function;   the
  887. second  parameter  determines whether  the  specified  flags  are
  888. enabled  (cfoENABLE),  disabled  (cfoDISABLE),  or  intended   to
  889. overwrite  all  of the current flags (cfoOVERWRITE)  as  well  as
  890. whether   the   changes  are  made  for  the  first   AlertDriver
  891. (cfoDRIVER)   or   for   all   linked  AlertDrivers   (cfoCHAIN).
  892. Please see the Class Library Reference for more details.
  893.  
  894. Example  15  -  Enabling  and  disabling  AlertDriver  processing
  895. flags  for  an  Integer  object. Notice  that  when  enabling  or
  896. disabling   specific  flags,  other  flags  for  the  AlertDriver
  897. are   not   altered.   In   this  example,   the   first   linked
  898. AlertDriver   for  object  "a"  is  assumed  to  be   a   screen-
  899. oriented interactive AlertDriver; this is normally the case.
  900.  
  901. ...
  902. Integer a;
  903. ...
  904. /*temporarily disable information alert
  905.    processing to the screen for a*/
  906. a.ChangeAlertProcFlags(adfINFO, cfoDRIVER | cfoDISABLE);
  907. /*now perform operations on a which are likely to
  908.   generate information alerts*/
  909. ...
  910. //enable information alert processing for a
  911. a.ChangeAlertProcFlags(adfINFO, cfoDRIVER | cfoENABLE);
  912. ...
  913.  
  914. The   adfALLALERTS  flag  is  a  combination  of  the   adfERROR,
  915. adfINFO, adfMESSAGE, and adfWARNING flags.
  916.  
  917. The   adfTIMESTAMP   flag   controls   whether   an   alert    is
  918. timestamped  when  reported.  When  enabled,  this  flag   causes
  919. the   AlertDriver  to  generate  a  string  holding  the  current
  920. date/time  and  appends  the  alert  text  to  that  string.  The
  921. timestamp  is  formatted  with  the  standard  ANSI  C   function
  922. ctime()   (see   the   Class  Library   Reference   chapter   for
  923. details.)  A  typical  timestamped error message  output  through
  924. a TextFileAlertDriver might look like:
  925.  
  926. Tue Dec 28 15:18:36 1993
  927. ERROR - Literal:  This is an error string.
  928.  
  929. By  this  point,  you've  no doubt realized  that  a  warning  is
  930. the  only  type  of  alert which needs to return  information  to
  931. the      program.      When     calling      member      function
  932. AlertableObject::HandleWarning(),  you   not   only   specify   a
  933. string  containing  a  question  for  the  user,  but  you   also
  934. indicate  the  default  response  to  the  question  (please  see
  935. the  Class  Library  Reference chapter for details).  If  warning
  936. processing   is   enabled   for   the   object's   first   linked
  937. AlertDriver,  the  question is presented  to  the  user  and  the
  938. response  is  returned.  However,  this  is  one  of  only  three
  939. possible  states  for  the AlertDriver;  warning  processing  may
  940. be   disabled  for  the  AlertDriver,  or  the  object  may   not
  941. currently  be  attached to an AlertDriver. In  the  latter  case,
  942. AlertableObject::HandleWarning()     returns      a      constant
  943. indicating  that  no  AlertDriver  is  attached  to  the  object.
  944. But what should be returned in the former case?
  945.  
  946. By   default,   if  warning  processing  is  disabled   for   the
  947. AlertDriver,  the  value  returned  is  the  default  value   you
  948. specified.  Your  program  can assume that  the  user  chose  the
  949. default   option  and  execution  continues  normally.   However,
  950. there  may  be  times when you want to know  that  the  user  was
  951. not  given  the  opportunity to make  a  choice.  In  this  case,
  952. you   should  enable  processing  flag  adfDISCLOSURE   for   the
  953. AlertDriver.  When  adfDISCLOSURE is enabled  and  adfWARNING  is
  954. disabled,   calls   to  AlertableObject::HandleWarning()   result
  955. in   a   special   constant   being   returned;   this   constant
  956. indicates   that   processing   for   the   current   alert   was
  957. disabled.
  958.  
  959. Please          see          the          discussion           of
  960. AlertableObject::HandleWarning()    in    the    Class    Library
  961. Reference   for   full   details  of   that   member   function's
  962. possible  return  values  and  how your  code  should  deal  with
  963. them.
  964.  
  965. In  most  cases,  the  default  settings  of  each  AlertDriver's
  966. processing   flags  should  be  adequate  for  your  application.
  967. Remember,   when   setting   the   processing   flags   for    an
  968. AlertDriver  which  is  shared  between  objects,  you  will   be
  969. affecting the alert processing for all of those objects.
  970.  
  971.  
  972. What About Exception Handling?
  973.  
  974. Because   exception   handling  (a  fairly   new   C++   language
  975. feature)  is  not  supported  by  all  C++  compilers  (and   not
  976. implemented   consistently  among  those   compilers   which   do
  977. support  it),  we  will  not attempt  to  show  sample  code  for
  978. using  this  feature.  However,  this  does  not  mean  that  the
  979. AlertDriver   Class  Library  is  incompatible   with   exception
  980. handling concepts.
  981.  
  982. Exception  handling  allows  for the separation  of  a  program's
  983. error-generation    code    from   the   error-reporting/handling
  984. code.   This   is   implemented  through  a  try   block   (which
  985. contains  a  program's/routine's  "normal"  code)  and   one   or
  986. more  catch  blocks  (which handle the  errors  occurring  within
  987. the  try  block.)  Whenever a program exception  (an  exceptional
  988. condition,  such  as  an  error) occurs  within  the  try  block,
  989. the   program  executes  a  throw  (raises  the  exception)   and
  990. control is passed to one of the catch blocks.
  991.  
  992. Once  the  exception  reaches  the catch  block,  the  exception-
  993. handling  code  can  be  reported using an AlertDriver.  Even  if
  994. your   objects  have  been  written  to  throw  exceptions,  they
  995. themselves  can  catch the exceptions, report  them  using  their
  996. linked   AlertDriver(s),   then  re-throw   the   exceptions   to
  997. higher-level code.
  998.  
  999. If  you  plan  to  use exception handling in your  C++  programs,
  1000. we  suggest  that  you use the AlertDriver  Class  Library  as  a
  1001. environment-  and  device-independent means  of  reporting  those
  1002. exceptions.  The  concepts  of  the  AlertDriver  Class   Library
  1003. are a natural supplement to those of exception handling.
  1004.  
  1005.  
  1006. Application Design Techniques - A Summary
  1007.  
  1008. By  now,  you've  read  all about the power  and  flexibility  of
  1009. the  AlertDriver  Class  Library.  Compiled  below  are  what  we
  1010. have   found   to  be  the  most  important  coding  and   design
  1011. techniques   to   use   when   writing   applications   for   the
  1012. AlertDriver Class Library.
  1013. 1.Always  invoke  the  appropriate  handling  for  an  alert   in
  1014.   the   member   function   in   which   the   alert   is   first
  1015.   generated;  this  helps  make your  objects  self-contained  by
  1016.   allowing them to process their own alerts.
  1017. 2.Whenever   possible,  return  error  codes  from  your   member
  1018.   functions.  This  practice  allows calling  code  to  determine
  1019.   if your member functions were successful.
  1020. 3.Use   the  preferred  method  of  alerting  whenever  possible.
  1021.   This  requires  that  your  class be  designed  so  that  alert
  1022.   constants   can   be   defined  early   in   development,   but
  1023.   rewards  you  with  highly  reusable code.  By  following  this
  1024.   practice,   the   text   for   each   alert   can   be   stored
  1025.   externally and/or altered by descendant classes.
  1026. 4.Use   positive   or   negative  numbers  to   represent   alert
  1027.   constants,  but  never  use  zero  (0).  Zero  should  indicate
  1028.   that nothing is wrong.
  1029. 5.Always   try  to  share  AlertDrivers  among  objects  whenever
  1030.   possible,   as  this  minimizes  memory  usage.  However,   you
  1031.   should  give  an  object  its own (non-shared)  AlertDriver  if
  1032.   you  will  be  altering  its processing  flags;  this  practice
  1033.   will  prevent  the  inadvertent processing  changes  of  alerts
  1034.   from other objects.
  1035. 6.Use   the  AlertDriver  referenced  by  defaultAlertDriver  for
  1036.   environment-independent   alerting   for    non-object-oriented
  1037.   code.
  1038. 7.Always  create  AlertDrivers on the  heap.  When  they  are  no
  1039.   longer   used,  they  will  automatically  be  deallocated   by
  1040.   the AlertableObject(s) which use them.
  1041. 8.Link   AlertDrivers  whenever  you  need  to  provide  alerting
  1042.   to   multiple  output  devices.  Linking  can  take  place   at
  1043.   compile-time or at run-time.
  1044. 9.Modify   an   AlertDriver's  processing  flags   whenever   you
  1045.   need  to  enable  or  disable the output of  certain  types  of
  1046.   alerts   either  temporarily  or  for  the  object's  lifetime.
  1047.   Processing  flags  can be altered at compile-time  or  at  run-
  1048.   time.
  1049.  
  1050.  
  1051.  
  1052. Chapter 4           Compiling & Linking
  1053.                     
  1054. IMPORTANT:       This  chapter  describes  how  to  compile   and
  1055.           link   your  programs  to  use  the  AlertDriver  Class
  1056.           Library.   You   should  read  this  section   in   its
  1057.           entirety    before   using   the   AlertDriver    Class
  1058.           Library.
  1059.  
  1060.  
  1061. Supported Compilers & Class Libraries
  1062.  
  1063. As  of  the  printing  of  this  manual,  the  AlertDriver  Class
  1064. Library   has   been  successfully  tested  with  the   following
  1065. compilers/class libraries:
  1066.   Borland   C++   v3.1  (text  mode,  Container  Class   Library,
  1067.   Turbo Vision, ObjectWindows)
  1068.   Microsoft C/C++ v7.0 (text mode)
  1069.  
  1070. Other  untested  compilers/class libraries may  be  supported  in
  1071. text   mode.   You   should   read  the   supplied   file   named
  1072. "README.1ST"   on   the  distribution  disks   for   the   latest
  1073. information  which  may  describe  features/updates  added  after
  1074. the printing of this manual.
  1075.  
  1076.  
  1077. Header Files
  1078.  
  1079. There  are  four  header files associated  with  the  AlertDriver
  1080. Class Library:
  1081. ADWINDLL.H  Prototypes    of   functions   used   in    Microsoft
  1082.             Windows helper DLL.
  1083. ALERTDRV.H  AlertDriver       class       (and       derivatives)
  1084.             declarations.
  1085. ALERTOBJ.H  AlertableObject class declaration.
  1086. ENVIRON.H   Compilation- and target-specific declarations.
  1087.  
  1088. You  will  normally  #include  the ALERTOBJ.H  and/or  ALERTDRV.H
  1089. header  files  in  your source code files to  give  the  compiler
  1090. the  necessary  class  declarations and/or  function  prototypes.
  1091. (The  Class  Library  Reference  chapter  lists  the  appropriate
  1092. header  file  for  each identifier which your  program  may  need
  1093. to access from the AlertDriver Class Library.)
  1094.  
  1095. The  ADWINDLL.H  header file is automatically  #included  by  the
  1096. AlertDriver  Class  Library whenever  you  compile  your  program
  1097. for  the  Microsoft  Windows  platform.  You  will  normally  not
  1098. need to be concerned with this file.
  1099.  
  1100. The  ENVIRON.H  header  file  is a special  file  which  contains
  1101. all   of   the   compilation-  and  target-specific  declarations
  1102. needed  to  compile  your  program  for  different  environments.
  1103. You  may  need  to  replace the first two (2) #define  statements
  1104. in   ENVIRON.H  to  compile  your  program  for  the  appropriate
  1105. environment   (see  below).  Don't  worry:   this  is   extremely
  1106. easy to do and the header file itself is clearly documented!
  1107.  
  1108.  
  1109. Changing The Compilation Environment #define in ENVIRON.H
  1110.  
  1111. The   first  #define  statement  in  the  ENVIRON.H  header  file
  1112. defines  the  compilation environment and  class  library  to  be
  1113. used.  You  should  replace the #define with  one  of  the  valid
  1114. entries  listed  below.  Your choice of  compilation  environment
  1115. will   cause   the   compiler   to   make   several   assumptions
  1116. (detailed   below)   about  your  code.  The  valid   compilation
  1117. environment #defines are:
  1118. BORLAND_CONTAIN        Borland's    Container   Class    Library.
  1119.                OBJECT.H    will    be    #included    and     the
  1120.                AlertDriver   Class  Library   classes   will   be
  1121.                derived from Object.
  1122. BORLAND_OWL    Borland's    ObjectWindows    Library.     Because
  1123.                Borland's  Windows  Custom  Controls  Library   is
  1124.                used   to   present  some  alerts  in   customized
  1125.                dialog    boxes,   BWCC.H   will   be    #included
  1126.                (BWCC.H   is   supplied  with  the   Borland   C++
  1127.                compiler).   OBJECT.H  will  also   be   #included
  1128.                and   the   AlertDriver  Class   Library   classes
  1129.                will be derived from Object.
  1130. BORLAND_TV     Borland's    Turbo    Vision    Class     Library.
  1131.                #defines     several    Turbo    Vision     Uses_*
  1132.                preprocessor   definitions,  then  #includes   the
  1133.                TV.H    header   file.   The   AlertDriver   Class
  1134.                Library  classes  will  be derived  from  TObject.
  1135.                When      you     #define     this     compilation
  1136.                environment,     you    should    #define     your
  1137.                program's   Turbo   Vision   Uses_*   preprocessor
  1138.                directives   before   #including   any   of    the
  1139.                AlertDriver  Class  Library  header   files.   See
  1140.                the    Turbo   Vision   documentation   for   more
  1141.                details.
  1142. NOROOT         The  AlertDriver  Class Library  classes  will  be
  1143.                compiled without a common root class.
  1144.  
  1145.  
  1146. Changing The Target Environment #define In ENVIRON.H
  1147.  
  1148. The  second  #define  statement  in  the  ENVIRON.H  header  file
  1149. defines  the  target environment for the executable  file  to  be
  1150. produced.  You  should  replace  the  #define  with  one  of  the
  1151. valid    entries   listed   below.   Your   choice   of    target
  1152. environment   will   cause   the   compiler   to   make   several
  1153. assumptions   (detailed  below)  about  your  code.   The   valid
  1154. target environment #defines are:
  1155. TARGET_DOS     The  program  will  run under  the  Microsoft  DOS
  1156.                (or compatible) operating system.
  1157. TARGET_WINDOWS The   program   will  run  under   the   Microsoft
  1158.                Windows       (or      compatible)       operating
  1159.                environment.    Header    file    ADWINDLL.H    is
  1160.                #included      (this     indirectly      #includes
  1161.                WINDOWS.H).   When   you   #define   this   target
  1162.                environment,     you    should    #define     your
  1163.                program's    WINDOWS.H   preprocessor   directives
  1164.                (if    any)   before   #including   any   of   the
  1165.                AlertDriver Class Library header files.
  1166.  
  1167. Of  course,  not  every  combination of  compilation  environment
  1168. and   target  environment  is  valid,  so  make  sure  that   the
  1169. combination   you  choose  makes  sense  (if  it   doesn't,   the
  1170. compiler  will  usually  give you an  error).  For  example,  the
  1171. #defines shown here are illegal:
  1172.  
  1173. #define BORLAND_OWL   //use ObjectWindows Library
  1174. #define TARGET_DOS    //ILLEGAL - cannot compile OWL apps as
  1175. DOS apps
  1176.  
  1177. The  correct  settings  for  ENVIRON.H  for  this  example  would
  1178. be:
  1179.  
  1180. #define BORLAND_OWL      //use ObjectWindows Library
  1181. #define TARGET_WINDOWS   //OWL apps must be Windows apps
  1182.  
  1183.  
  1184. Linking Your Programs
  1185.  
  1186. To  link  your  programs, you must link the  appropriate  library
  1187. to  your  application  in  your MAKE or PROJECT  file  (see  your
  1188. compiler  manual  for details on linking).  Refer  to  the  lists
  1189. below   for   the   appropriate  library  for  your   compilation
  1190. environment, target environment, and memory model.
  1191.  
  1192. IMPORTANT -  Make  sure  you  link  your programs  to  the  right
  1193.           library.   If   you  link  the  wrong   library,   your
  1194.           program will not execute.
  1195.  
  1196. If   you   #defined  TARGET_DOS  in  ENVIRON.H  as  your   target
  1197. environment:
  1198.      Link  with  one  of  the following libraries,  whichever  is
  1199.      appropriate   for   the  setting  you   #defined   for   the
  1200.      compilation  environment  in ENVIRON.H  and  your  program's
  1201.      memory model:
  1202.           ADBRDCCx.LIBBorland  C++  Container  Classes  -   There
  1203.                       are  six  of  these files, where  x  =  the
  1204.                       first    letter   of   the   memory   model
  1205.                       (tiny,   small,  medium,  compact,   large,
  1206.                       or   huge).   For   example,   ADBRDCCL.LIB
  1207.                       is   for   use  with  large  memory   model
  1208.                       programs.
  1209.           ADBRDNRx.LIBBorland   C++,  no  root  class   -   There
  1210.                       are  six  of  these files, where  x  =  the
  1211.                       first    letter   of   the   memory   model
  1212.                       (tiny,   small,  medium,  compact,   large,
  1213.                       or   huge).   For   example,   ADBRDNRL.LIB
  1214.                       is   for   use  with  large  memory   model
  1215.                       programs.
  1216.           ADBRDTVL.LIBBorland   C++   Turbo   Vision   -    large
  1217.                       memory model.
  1218.           ADMSDNRx.LIBMicrosoft   C/C++,   no   root   class    -
  1219.                       There   are  four  of  these  files,  where
  1220.                       x   =   the  first  letter  of  the  memory
  1221.                       model    (small,   medium,   compact,    or
  1222.                       large).   For   example,  ADMSDNRL.LIB   is
  1223.                       for    use   with   large   memory    model
  1224.                       programs.
  1225.      If    you    #defined   BORLAND_TV   as   the    compilation
  1226.      environment, link this file:
  1227.           TV.LIB      Borland's     Turbo     Vision      library
  1228.                       (supplied    with    the    Borland     C++
  1229.                       compiler).
  1230.  
  1231. If  you  #defined  TARGET_WINDOWS in  ENVIRON.H  as  your  target
  1232. environment:
  1233.      Link  with  one  of  the following libraries,  whichever  is
  1234.      appropriate   for   the  setting  you   #defined   for   the
  1235.      compilation  environment  in ENVIRON.H  and  your  program's
  1236.      memory model:
  1237.           ADBRWCCx.LIBBorland  C++  Container  Classes  -   There
  1238.                       are   four  of  these  files,  where  x   =
  1239.                       the   first  letter  of  the  memory  model
  1240.                       (small,   medium,   compact,   or   large).
  1241.                       For   example,  ADBRWCCL.LIB  is  for   use
  1242.                       with large memory model programs.
  1243.           ADBRWNRx.LIBBorland   C++,  no  root  class   -   There
  1244.                       are   four  of  these  files,  where  x   =
  1245.                       the   first  letter  of  the  memory  model
  1246.                       (small,   medium,   compact,   or   large).
  1247.                       For   example,  ADBRDNRL.LIB  is  for   use
  1248.                       with large memory model programs.
  1249.           ADBRWOWx.LIBBorland   C++   ObjectWindows   Library   -
  1250.                       There   are  four  of  these  files,  where
  1251.                       x   =   the  first  letter  of  the  memory
  1252.                       model    (small,   medium,   compact,    or
  1253.                       large).   For   example,  ADBRWOWL.LIB   is
  1254.                       for    use   with   large   memory    model
  1255.                       programs.
  1256.      Also link this file:
  1257.           ADWINDLL.LIBImport     library     for     the     file
  1258.                       ADWINDLL.DLL;      make      sure       you
  1259.                       distribute    ADWINDLL.DLL    with     your
  1260.                       finished application!
  1261.      If    you    #defined   BORLAND_OWL   as   the   compilation
  1262.      environment, link this file:
  1263.           BWCC.LIB    Import   library   for   Borland's   Custom
  1264.                       Control   Library  for  Windows   (supplied
  1265.                       with   the  Borland  C++  compiler);   make
  1266.                       sure    you   distribute   BWCC.DLL    with
  1267.                       your application!
  1268.  
  1269.  
  1270. Compiler-Specific Notes
  1271.  
  1272. When  writing  programs  which  use  the  AlertDriver  C++  Class
  1273. Library,  you  may  need  to know how the  supplied  object  code
  1274. was  compiled.  The  notes below indicate the  compiler  settings
  1275. used  to  compile  the AlertDriver Class Library.  You  may  want
  1276. to   consider   purchasing  the  Source  Code  Edition   of   the
  1277. AlertDriver  C++  Class  Library if you  want  to  recompile  the
  1278. AlertDriver Class Library with different compiler settings.
  1279.  
  1280.  
  1281. Borland C++ v3.1
  1282.  
  1283. All  object  code  libraries  and  DLLs  were  compiled  assuming
  1284. that   all   unsigned   char  variables  should   be   explicitly
  1285. defined  (unsigned  chars off). Stack checking  was  turned  off.
  1286. DOS   libraries   were  compiled  with  8086/8088   instructions.
  1287. Windows   libraries   were  compiled  with  80286   instructions,
  1288. target  Windows  3.0  and  above,  and  assume  the  use  of  the
  1289. static   versions  of  the  standard,  Container  Class,   and/or
  1290. ObjectWindows libraries.
  1291.  
  1292.  
  1293. Microsoft C/C++ v7.0
  1294.  
  1295. Stack   checking   was   turned  off  and  the   libraries   were
  1296. compiled with 8086 instructions.
  1297.  
  1298.  
  1299. Compiling The Sample Programs
  1300.  
  1301. To  compile  any  of the sample programs, you should  follow  all
  1302. of  the  rules  and  suggestions  previously  mentioned  in  this
  1303. chapter.  Don't  forget  to  correctly #define  the  compilation-
  1304. and target-environment macros in ENVIRON.H!
  1305.  
  1306. The  sample  file  INTDEMO.CPP is the main file  for  the  sample
  1307. program   using  the  Integer  class.  (The  Integer   class   is
  1308. discussed  in  the  Tutorial Chapter.) To compile  this  program,
  1309. you   should  compile  the  INTDEMO.CPP  and  INTEGER.CPP  files,
  1310. then  link  them  with the appropriate AlertDriver  Library  .LIB
  1311. file(s).   These   files  can  be  compiled  for   any   of   the
  1312. supported compilation- and/or target-environments.
  1313.  
  1314. If  you  want  to  compile the INTDEMO.CPP sample  program  as  a
  1315. Windows   application,  you  should  also  link   the   ADWIN.DEF
  1316. module definition file to your executable.
  1317.  
  1318. Note   that   the   source  code  for  the  Integer   class   (in
  1319. INTEGER.CPP),  and  the  INTDEMO.CPP functions  which  manipulate
  1320. the   Integer  objects,  are  totally  compilation-  and  target-
  1321. environment independent.
  1322.  
  1323.  
  1324.  
  1325. Class Hierarchy
  1326.  
  1327. In   the   listing   below,  derivative  classes   are   indented
  1328. underneath    their    parent   class.   For    example,    class
  1329. WindowsAlertDriver  is  derived  from  class  AlertDriver,  which
  1330. is derived from class AlertDriverLink.
  1331.  
  1332. ROOTCLASS
  1333.    AlertDriverLink
  1334.       AlertableObject
  1335.       AlertDriver
  1336.          RecordingAlertDriver
  1337.             StreamAlertDriver
  1338.                TextFileAlertDriver
  1339.          StdAlertDriver
  1340.          TurboVisionAlertDriver
  1341.          WindowsAlertDriver
  1342.             BWCCAlertDriver
  1343.  
  1344.  
  1345.  
  1346. Chapter 6           Creating Your Own AlertDrivers
  1347.                     
  1348.  
  1349. Design Concepts
  1350.  
  1351. For  most  of  your  programming projects,  the  AlertDriver
  1352. classes  shipped with the AlertDriver Class Library will  be
  1353. sufficient  for your needs. If, however, you would  like  to
  1354. output  alerts using operating-system API calls or  hardware
  1355. devices  which are not supported by the standard AlertDriver
  1356. Class Library, then you will need to create (subclass)  your
  1357. own  AlertDriver classes. You may also want to subclass  the
  1358. supplied  AlertDrivers to change their default behavior.  In
  1359. any  case, this chapter presents the guidelines which should
  1360. be  followed  while  designing and writing  new  AlertDriver
  1361. classes.
  1362.  
  1363. Before  designing  your  own custom AlertDriver  class,  you
  1364. should  first determine what type of AlertDriver  the  class
  1365. will  be.  There are two basic types of AlertDriver classes:
  1366. interactive  (those which can interact with  the  user)  and
  1367. recording   (those  which  provide  output-only   services).
  1368. Interactive  AlertDriver  classes  are  derived   from   the
  1369. AlertDriver   class,   while  recording   AlertDrivers   are
  1370. subclassed from the RecordingAlertDriver class.
  1371.  
  1372. Because  interactive AlertDrivers usually report  alerts  to
  1373. the  screen, they should be designed to use standard C, C++,
  1374. or  operating-system API calls whenever possible.  Recording
  1375. AlertDrivers,  however, usually report to  hardware  devices
  1376. (which are somewhat dependent on the operating-system)  such
  1377. as   printers  or  RS-232C  devices.  Therefore,   recording
  1378. AlertDrivers  often must be designed to use  the  API  calls
  1379. provided by the hardware device manufacturer.
  1380.  
  1381.  
  1382. Requirements & Suggestions
  1383.  
  1384. To  write an AlertDriver class definition, you must  have  a
  1385. good understanding of the API calls necessary to control the
  1386. output  resource,  as  well  as  an  understanding  of   the
  1387. class(es)   from   which  you  will   be   subclassing   the
  1388. AlertDriver.  Of course, you must also have a  C++  compiler
  1389. for  your  target environment, as well as any  prerequisites
  1390. for the compiler.
  1391.  
  1392. If  you are planning to create your own AlertDriver classes,
  1393. we  recommend that you purchase the Source Code  Edition  of
  1394. the  AlertDriver  Class  Library.  By  examining  the  well-
  1395. commented source code used to write the supplied AlertDriver
  1396. classes,  you  can gain a much deeper understanding  of  the
  1397. techniques used to write reusable AlertDrivers.
  1398.  
  1399.  
  1400. Updating ENVIRON.H
  1401.  
  1402. You will have to update the ENVIRON.H header file if you are
  1403. trying to do one of the following:
  1404. 1.port  the  AlertDriver Class Library to a new  compilation
  1405.   environment (i.e.:  use a currently-unsupported  hierarchy
  1406.   class   as  the  root  class  for  the  AlertDriver  Class
  1407.   Library)
  1408. 2.port  the  AlertDriver  Class  Library  to  a  new  target
  1409.   environment  (i.e.:   target your executable  code  for  a
  1410.   currently-unsupported operating system)
  1411. 3.redefine  one  of  the  macros specifying  an  AlertDriver
  1412.   system   setting  (i.e.:   specify  a  different   default
  1413.   AlertDriver  or change the maximum size of an  alert  text
  1414.   string)
  1415.  
  1416. If  you need to update ENVIRON.H, we suggest you first print
  1417. the   file  and  study  its  heavily-commented  preprocessor
  1418. directives.
  1419.  
  1420.  
  1421. Defining Your AlertDriver
  1422.  
  1423. To  define  an AlertDriver, you must first plan to  override
  1424. the  AlertDriver::Report*() member functions. Remember  that
  1425. for     interactive    AlertDrivers,    member     functions
  1426. ReportError(), ReportInfo(), and ReportWarning() should halt
  1427. program  execution  until  acknowledged  by  the  user.  For
  1428. recording  AlertDrivers, program execution should  never  be
  1429. halted.  For  full details on the intended  actions  of  the
  1430. Report*()   member  functions  for  both   interactive   and
  1431. recording   AlertDrivers,  see  the   discussions   of   the
  1432. AlertDriver::Report*() member functions in the Class Library
  1433. Reference chapter.
  1434.  
  1435. If  you are writing a recording AlertDriver, you should call
  1436. member     function    GetResourceControl()    from     your
  1437. constructor(s), destructor, and Report*() member  functions.
  1438. If   the   resource  control  mode  is  radFREERESOURCE   or
  1439. radHOLDRESOURCE, your object should explicitly  capture  and
  1440. free  the  resource  when  appropriate.  (For  the  supplied
  1441. TextFileAlertDriver, this is accomplished by opening/closing
  1442. the   output   file.)   Review   the   discussion   of   the
  1443. RecordingAlertDriver  data  member  resourceControl  in  the
  1444. Class Library Reference for more details on controlling  the
  1445. output resource for a recording AlertDriver.
  1446.  
  1447. You      may      also     want     to     override      the
  1448. AlertDriver::GetTimestamp() member function  to  return  the
  1449. computer's current date/time in a format different from  the
  1450. default.
  1451.  
  1452. Finally,  if  your AlertDriver encounters  an  error  during
  1453. output,  you should not attempt to display an error message.
  1454. Just   clean   up   any  allocated  resources   and   return
  1455. immediately.
  1456.  
  1457. That's  all  there  is  to  creating  your  own  AlertDriver
  1458. objects!  If  you  have  followed  these  guidelines,   your
  1459. AlertDriver  objects should be usable by any AlertableObject
  1460. without changes to the AlertableObject.
  1461.